"""""""""""""""""""""""
""""""""游戏类"""""""""
""""""""""""""""""""""""

import setting as cfg
from Sprite import *
import math


class Game(object):
    '''初始化游戏类'''

    def __init__(self):
        pass

    ''' 游戏初始化 '''

    def game_init(self):

        # 游戏初始化
        pygame.init()
        # 设置游戏屏幕大小
        self.screen = pygame.display.set_mode((cfg.sc_width, cfg.sc_height))

        # 设置游戏标题
        pygame.display.set_caption("打砖块游戏")

        # 设置游戏字体
        self.font = pygame.font.Font(None, 36)

        # 设置游戏时钟
        self.timer = pygame.time.Clock()

        # 设置游戏相关精灵组
        self.paddle_group = pygame.sprite.Group()
        self.block_group = pygame.sprite.Group()
        self.ball_group = pygame.sprite.Group()

        # 加载挡板并设置位置
        self.paddle = Sprite(self.screen)
        self.paddle.load("pic/paddle.png")
        self.paddle.set_pos((250, 460))
        self.paddle_group.add(self.paddle)

        # 加载小球并设置位置
        ball = Sprite(self.screen)
        ball.load("pic/ball.png")
        self.x0 = self.paddle.get_x() + self.paddle.frame_width / 2 - ball.frame_width / 2
        self.y0 = self.paddle.get_y() - ball.frame_height
        ball.set_pos((self.x0, self.y0))
        self.ball_group.add(ball)
        self.ball_list = [ball]
        print("游戏初始化完毕")

    ''' 加载砖块 '''

    ''' 加载砖块 '''
    def load_level(self):
        #清空砖块精灵组
        self.block_group.empty()
        level_tuple = cfg.new_level(cfg.level)

        for by in range(0, 10):
            for bx in range(0, 10):
                #加载砖块并设置每个砖块的位置
                self.block = Sprite(self.screen)
                self.block.load("pic/blocks.png", 58, 29, 4)
                x = 5 + bx * (self.block.frame_width+1)
                y = 30 + by * (self.block.frame_height+1)
                self.block.set_pos((x, y))
                #检测不需要加载砖块的位置
                num = level_tuple[by*10+bx]
                self.block.first_frame = num - 1
                self.block.last_frame = num - 1
                if num > 0:
                    self.block_group.add(self.block)
        print("关卡加载完毕")

    ''' 进入下一关 '''

    def goto_next_level(self):
        cfg.level += 1
        if not cfg.level % 5:
            cfg.ball_num += 1
            print("奖励！当前小球数："+str(cfg.ball_num))
        if not cfg.level % 10:
            cfg.lives += 1
            print("奖励！当前生命数："+str(cfg.lives))
        self.load_level()
        print("已进入下一关")

    def load_ball(self):
        cfg.waiting = True
        self.ball_list.clear()
        for i in self.ball_group:
            self.ball_group.remove(i)
        interval = self.paddle.frame_width / (cfg.ball_num + 1)
        px = self.paddle.get_x()
        temp = cfg.ball_num
        for i in range(temp):
            ball = Sprite(self.screen)
            ball.load("pic/ball.png")
            ball.set_pos((px + (i + 1) * interval - ball.frame_width / 2, self.y0))
            self.ball_group.add(ball)
            self.ball_list.append(ball)
        print("小球加载完毕，当前共"+str(temp)+"个小球")

    ''' 
    更新砖块
    param {int} ticks 自pygame.init()调用以来的毫秒数
    '''

    def update_blocks(self, ticks):
        # 如果砖块数为0，进入下一关，重置小球位置和挡板位置
        if len(self.block_group) == 0:
            self.goto_next_level()
            cfg.waiting = True
        self.block_group.update(ticks, 50)

    '''
    移动挡板
    param {dict} keys 一个由布尔类型值组成的序列，表示键盘上所有按键的当前状态
    param {int} ticks 自pygame.init()调用以来的毫秒数
    '''

    def move_paddle(self, keys, ticks):
        self.paddle_group.update(ticks, 50)

        # 空格则重置小球速度，发射小球
        if keys[K_SPACE]:
            if len(self.ball_list) and cfg.waiting:
                cfg.waiting = False
                self.reset_ball()
        # 挡板向左运动
        elif keys[K_LEFT]:
            # 向左运动速度
            self.paddle.velocity.x = -10.0
        # 挡板向右运动
        elif keys[K_RIGHT]:
            # 向右运动速度
            self.paddle.velocity.x = 10.0
        # 挡板静止不动
        else:
            # 速度为0
            self.paddle.velocity.x = 0
        # 添加速度
        self.paddle.add_x(self.paddle.velocity.x)

        # 设置挡板的运动范围
        # 不能超过屏幕的左边，超过则重置挡板位置为最左侧
        if self.paddle.get_x() < 0:
            self.paddle.set_x(0)
        # 不能超过屏幕的右边，超过则重置挡板位置为最右侧
        elif self.paddle.get_x() > cfg.sc_width - self.paddle.frame_width:
            self.paddle.set_x(cfg.sc_width - self.paddle.frame_width)

    '''
    移动小球
    param {int} ticks 自pygame.init()调用以来的毫秒数
    '''

    def move_ball(self, ticks):
        if cfg.waiting:
            interval = self.paddle.frame_width / (cfg.ball_num + 1)
            px = self.paddle.get_x()
            length = len(self.ball_list)
            for i in range(length):
                self.ball_list[i].set_pos((px + (i + 1) * interval -
                                           self.ball_list[i].frame_width / 2, self.y0))
        else:
            for i in self.ball_list[:]:
                self.move_ball_called(ticks, i)
            if not cfg.ball_num:
                cfg.lives = cfg.lives - 1
                print("小球全部丢失，当前生命数："+str(cfg.lives))
                if cfg.lives < 1:
                    print("生命数为0 本轮结束")
                    cfg.game_over = True
                    cfg.waiting = True
                else:
                    # lives -1 本关重开
                    cfg.ball_num = 1
                    print("生命数未耗尽，小球数重置为"+str(cfg.ball_num))
                self.load_ball()

    def move_ball_called(self, ticks, ball):
        self.ball_group.update(ticks, 50)
        ball.add_x(ball.velocity.x)
        ball.add_y(ball.velocity.y)
        # 超出屏幕x轴范围，触边反弹，x轴方向速度反向
        if ball.get_x() < 0:
            ball.set_x(0)
            ball.velocity.x *= -1
        elif ball.get_x() > cfg.sc_width - ball.frame_width:
            ball.set_x(cfg.sc_width - ball.frame_width)
            ball.velocity.x *= -1
        # 超出屏幕上边，触边反弹，y轴方向速度反向
        if ball.get_y() < 0:
            ball.set_y(0)
            ball.velocity.y *= -1
        # 小球掉落，生命-1，判断游戏是否结束
        elif ball.get_y() > cfg.sc_height - ball.frame_height:
            self.ball_list.remove(ball)
            self.ball_group.remove(ball)
            cfg.ball_num -= 1
            print("小球丢失，当前小球数："+str(cfg.ball_num))

    ''' 重置小球速度 '''

    def reset_ball(self):
        for i in self.ball_list:
            i.velocity = Point(4 + 0.3 * math.log2(cfg.level+1), -5 - 0.3 * math.log2(cfg.level+1))

    ''' 检测小球与挡板的碰撞 '''

    def collision_ball_paddle(self):
        for i in self.ball_list:
            self.collision_ball_paddle_called(i)

    def collision_ball_paddle_called(self, ball):
        # 检测碰撞
        if pygame.sprite.collide_rect(ball, self.paddle):

            # y轴方向速度反向
            ball.velocity.y = -abs(ball.velocity.y)

            # 小球及挡板中心的x及y坐标
            bx = ball.get_x() + ball.frame_width / 2
            by = ball.get_y() + ball.frame_height / 2
            px = self.paddle.get_x() + self.paddle.frame_width / 2
            py = self.paddle.get_y() + self.paddle.frame_height / 2

            # x轴方向速度的设置
            if bx < px:
                ball.velocity.x = -abs(ball.velocity.x)
            else:
                ball.velocity.x = abs(ball.velocity.x)

    ''' 检测小球与砖块的碰撞 '''

    def collision_ball_blocks(self):
        for i in self.ball_list:
            self.collision_ball_blocks_called(i)

    def collision_ball_blocks_called(self, ball):
        # 检测碰撞的砖块
        hit_block = pygame.sprite.spritecollideany(ball, self.block_group)

        if hit_block != None:
            # 碰撞则加分
            cfg.score += 10
            # 移除碰撞到的砖块
            self.block_group.remove(hit_block)
            # 小球中心点的坐标
            bx = ball.get_x() + ball.frame_width / 2
            by = ball.get_y() + ball.frame_height / 2

            # 碰撞后小球的速度方向的设置
            if bx > hit_block.get_x() + ball.frame_width / 2 and \
                    bx < hit_block.get_x() + hit_block.frame_width - ball.frame_width / 2:
                if by < hit_block.get_y() + hit_block.frame_height:
                    ball.velocity.y = -abs(ball.velocity.y)
                else:
                    ball.velocity.y = abs(ball.velocity.y)
            elif bx < hit_block.get_x() + ball.frame_width / 2:
                ball.velocity.x = -abs(ball.velocity.x)
            elif bx > hit_block.get_x() + hit_block.frame_width - ball.frame_width / 2:
                ball.velocity.x = abs(ball.velocity.x)
            else:
                ball.velocity.y *= -1
